package coin

import (
	"global/core"
	"github.com/parnurzeal/gorequest"
	"global/constants"
	"global/libs/structs"
	"encoding/json"
	"github.com/ngaut/log"
	"time"
	"github.com/kataras/iris/core/errors"
	"srv/cmc/cap"
	"global/model"
	"sync"
	"strings"
	"fmt"
	"bytes"
)

type ListAllCoins struct {
	Ctx 		*core.Context
	Client 		*gorequest.SuperAgent
	Key 		string
	IntervalC 			*time.Ticker
	updateCoinTicker 	*time.Ticker

	coins 		map[string]*model.Coin
	mux 		sync.Mutex
}

func NewListAllCoins() cap.InterfaceCmc {
	return &ListAllCoins{
		// 1.5 分钟获取一次 Cmc coin 数据
		coins: 			make(map[string]*model.Coin),
		IntervalC: 		time.NewTicker(time.Second * 90),
		updateCoinTicker: 	time.NewTicker(time.Minute * 5),
	}
}

// 初始化
func (c *ListAllCoins) Init(ctx *core.Context) cap.InterfaceCmc {
	c.Ctx = ctx
	c.Client = gorequest.New()
	c.Key = c.Ctx.AccountConfig()

	// get coins
	c.getCoins()
	go c.updateCoins()

	return c
}

func (c *ListAllCoins) updateCoins()  {
	for t := range c.updateCoinTicker.C {
		_ = t
		c.getCoins()
	}
}

func (c *ListAllCoins) getCoins() {
	coins, err := model.CoinModel.FindAll()
	if err != nil {
		log.Errorf("Error: %s", err)
		return
	}

	coinMaps := make(map[string]*model.Coin)
	for _, v := range coins {
		coinMaps[strings.ToLower(v.Symbol)] = v
	}

	c.mux.Lock()
	c.coins = coinMaps
	c.mux.Unlock()
}

func (c *ListAllCoins) Run() error {
	go func() {
		c.handler()
		for t := range c.IntervalC.C {
			_ = t
			err := c.handler()
			if err != nil {
				log.Errorf("Error: %s", err.Error())
			}
		}
	}()
	return nil
}

func (c *ListAllCoins) handler() error {

	start := time.Now()
	log.Infof("start spider all coins info from cmc.")

	_, body, errs := c.Client.
		Get(core.GetCmcUrl(c.Ctx.IsDebug(), constants.CmcListAllLatestCurrencyUrl)).
		Set("X-CMC_PRO_API_KEY", c.Key).
		Query(map[string]interface{}{
			"limit": 5000,
			"convert": "USD,CNY,BTC",
		}).
		End()

	if len(errs) != 0 {
		return errs[0]
	}

	type CmcData struct {
		Data 		[]structs.MarketQuote 		`json:"data"`
		Status		structs.CmcStatus 			`json:"status"`
	}

	var parsedCmcDatas CmcData
	err := json.Unmarshal([]byte(body), &parsedCmcDatas)
	if err != nil {
		log.Errorf("Error: %s", err.Error())
		return err
	}

	if parsedCmcDatas.Status.ErrorCode != 0 {
		return errors.New(parsedCmcDatas.Status.ErrorMessage)
	}

	conn := c.Ctx.GetBaseRedis().Get()
	defer conn.Close()

	log.Infof("all count: %d", len(parsedCmcDatas.Data))
	err = core.SaveCoinMarketInfo(conn, parsedCmcDatas.Data)
	if err != nil {
		return err
	}

	// 更新 coin 表
	for _, v := range parsedCmcDatas.Data {
		c.mux.Lock()
		if _, ok := c.coins[strings.ToLower(v.Symbol)]; ok {

			b := bytes.NewBufferString(v.DateAdded)
			dateT, _ := b.ReadBytes('T')

			// update
			d := &model.Coin{
				MaxSupply: 			fmt.Sprintf("%.8f", v.MaxSupply),
				CirculatingSupply:	fmt.Sprintf("%.8f", v.CirculatingSupply),
				DateAdded: 			strings.TrimRight(string(dateT), "T"),
			}
			if _, err := model.CoinModel.UpdateCoin(v.Id, d); err != nil {
				log.Errorf("Error: %v", err)
			}
		}
		c.mux.Unlock()
	}

	log.Infof("spider success, coins count: %d, take time: %s",
		len(parsedCmcDatas.Data), time.Since(start))
	return nil
}